#include "raylib.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/wait.h>
#include <unistd.h>

// --------------------------
// Configuration (Adjust Me!)
// --------------------------
#define WIDTH 800
#define HEIGHT 400
#define PADDING 15
#define FONT_SIZE 36
#define MAX_ITEMS 2000
#define MAX_INPUT_LEN 256
#define MAX_VISIBLE 10
// System font path (update this to match your OS/font)
// Examples for common systems:
// - Linux: "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" (monospace)
// - Linux: "/usr/share/fonts/truetype/ubuntu/UbuntuMono-R.ttf" (Ubuntu default)
// - macOS: "/Library/Fonts/Menlo.ttc" (macOS default monospace)
#define SYSTEM_FONT_PATH "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf"

// Menu item structure
typedef struct
{
    char text[256];
    bool is_match;
} MenuItem;

// Global state
MenuItem items[MAX_ITEMS];
int item_count = 0;
char input_str[MAX_INPUT_LEN] = "";
int selected_idx = 0;
int scroll_offset = 0;
bool running = true;
size_t cursor = 0;
Font app_font; // Will hold the system font (or default fallback)

/**
 * Read items from stdin (e.g., rmenu_path output)
 */
void read_items_from_stdin()
{
    char buf[256];
    item_count = 0;
    while (fgets(buf, sizeof(buf), stdin) && item_count < MAX_ITEMS)
    {
        size_t len = strlen(buf);
        while (len > 0 && isspace((unsigned char)buf[len - 1]))
            buf[--len] = '\0';
        strncpy(items[item_count].text, buf, sizeof(items[item_count].text) - 1);
        items[item_count].is_match = true;
        item_count++;
    }
}

/**
 * Filter items by executable name (fixes rmenu self-detection)
 */
void filter_items()
{
    int input_len = strlen(input_str);
    if (input_len == 0)
    {
        for (int i = 0; i < item_count; i++)
            items[i].is_match = true;
        selected_idx = 0;
        scroll_offset = 0;
        return;
    }

    int match_count = 0;
    int first_match_idx = -1;

    for (int i = 0; i < item_count; i++)
    {
        bool match = false;
        const char *basename = strrchr(items[i].text, '/');
        basename = (basename) ? basename + 1 : items[i].text;
        int basename_len = strlen(basename);

        if (basename_len >= input_len)
        {
            match = true;
            for (int j = 0; j < input_len; j++)
            {
                if (tolower((unsigned char)input_str[j]) != tolower((unsigned char)basename[j]))
                {
                    match = false;
                    break;
                }
            }
        }

        items[i].is_match = match;
        if (match)
        {
            match_count++;
            if (first_match_idx == -1)
                first_match_idx = i;
        }
    }

    if (match_count == 0)
        selected_idx = -1;
    else
    {
        selected_idx = first_match_idx;
        int center_pos = MAX_VISIBLE / 2;
        scroll_offset = selected_idx - center_pos;
        if (scroll_offset < 0)
            scroll_offset = 0;
    }
}

/**
 * Execute command (no error reporting)
 */
void execute_command(const char *cmd)
{
    if (!cmd || strlen(cmd) == 0)
        return;

    pid_t pid = fork();
    if (pid == -1)
        return;

    if (pid == 0)
    {
        char *args[] = {(char *)cmd, NULL};
        execvp(args[0], args);
        exit(EXIT_FAILURE);
    }
    else
    {
        int status;
        waitpid(pid, &status, 0);
        running = false;
    }
}

/**
 * Handle keyboard input
 */
void handle_input()
{
    bool ctrl_pressed = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL);

    if (IsKeyPressed(KEY_ESCAPE))
    {
        running = false;
        return;
    }

    int key = GetCharPressed();
    while (key > 0)
    {
        if (key >= 32 && key <= 126 && strlen(input_str) < MAX_INPUT_LEN - 1)
        {
            memmove(&input_str[cursor + 1], &input_str[cursor], MAX_INPUT_LEN - cursor - 1);
            input_str[cursor++] = (char)key;
            filter_items();
        }
        key = GetCharPressed();
    }

    if (IsKeyPressed(KEY_BACKSPACE) && cursor > 0)
    {
        cursor--;
        memmove(&input_str[cursor], &input_str[cursor + 1], MAX_INPUT_LEN - cursor - 1);
        filter_items();
    }

    int center_pos = MAX_VISIBLE / 2;
    if (IsKeyPressed(KEY_DOWN) || (ctrl_pressed && IsKeyPressed(KEY_N)))
    {
        if (selected_idx == -1)
            return;
        int next_idx = selected_idx + 1;
        while (next_idx < item_count && !items[next_idx].is_match)
            next_idx++;
        if (next_idx < item_count)
        {
            selected_idx = next_idx;
            scroll_offset = selected_idx - center_pos;
            if (scroll_offset < 0)
                scroll_offset = 0;
        }
    }

    if (IsKeyPressed(KEY_UP) || (ctrl_pressed && IsKeyPressed(KEY_P)))
    {
        if (selected_idx == -1)
            return;
        int prev_idx = selected_idx - 1;
        while (prev_idx >= 0 && !items[prev_idx].is_match)
            prev_idx--;
        if (prev_idx >= 0)
        {
            selected_idx = prev_idx;
            scroll_offset = selected_idx - center_pos;
            if (scroll_offset < 0)
                scroll_offset = 0;
        }
    }

    if (IsKeyPressed(KEY_RIGHT) || (ctrl_pressed && IsKeyPressed(KEY_F)))
        if (cursor < strlen(input_str))
            cursor++;

    if (IsKeyPressed(KEY_LEFT) || (ctrl_pressed && IsKeyPressed(KEY_B)))
        if (cursor > 0)
            cursor--;

    if (IsKeyPressed(KEY_HOME) || (ctrl_pressed && IsKeyPressed(KEY_A)))
        cursor = 0;

    if (IsKeyPressed(KEY_END) || (ctrl_pressed && IsKeyPressed(KEY_E)))
        cursor = strlen(input_str);

    if (IsKeyPressed(KEY_DELETE) || (ctrl_pressed && IsKeyPressed(KEY_D)))
    {
        if (cursor < strlen(input_str))
            memmove(&input_str[cursor], &input_str[cursor + 1], MAX_INPUT_LEN - cursor - 1);
        filter_items();
    }

    if (ctrl_pressed && IsKeyPressed(KEY_K))
    {
        input_str[cursor] = '\0';
        filter_items();
    }

    if (IsKeyPressed(KEY_ENTER))
    {
        const char *cmd = NULL;
        if (selected_idx != -1 && items[selected_idx].is_match)
            cmd = items[selected_idx].text;
        else if (strlen(input_str) > 0)
            cmd = input_str;

        if (cmd)
            execute_command(cmd);
    }
}

/**
 * Render main window (uses system font)
 */
void render_main_window()
{
    BeginDrawing();
    ClearBackground(BLACK);

    // Input box
    DrawRectangle(PADDING, PADDING, WIDTH - 2 * PADDING, FONT_SIZE + 12, DARKGRAY);

    // Prompt text ("Filter > ")
    const char *prompt_text = "Filter > ";
    DrawTextEx(
        app_font, // Use loaded system font
        prompt_text,
        (Vector2){PADDING + 8, PADDING + 6},
        FONT_SIZE,
        1, // Character spacing (prevents cramped text)
        WHITE);
    float prompt_width = MeasureTextEx(app_font, prompt_text, FONT_SIZE, 1).x;

    // User input text
    DrawTextEx(
        app_font,
        input_str,
        (Vector2){PADDING + 8 + prompt_width, PADDING + 6},
        FONT_SIZE,
        1,
        YELLOW);

    // Text cursor
    char cursor_substr[MAX_INPUT_LEN];
    strncpy(cursor_substr, input_str, cursor);
    cursor_substr[cursor] = '\0';
    float cursor_x = prompt_width + PADDING + 8 +
                     MeasureTextEx(app_font, cursor_substr, FONT_SIZE, 1).x;
    DrawRectangle(cursor_x, PADDING + 6, 2, FONT_SIZE, WHITE);

    // Separator line
    int separator_y = PADDING + FONT_SIZE + 20;
    DrawLine(PADDING, separator_y, WIDTH - PADDING, separator_y, GRAY);

    // Render filtered items
    int y_offset = separator_y + 10;
    int visible_count = 0;
    int match_count = 0;
    for (int i = 0; i < item_count; i++)
        if (items[i].is_match)
            match_count++;

    for (int i = 0; i < item_count && visible_count < MAX_VISIBLE; i++)
    {
        if (items[i].is_match)
        {
            if (i < scroll_offset)
                continue;

            // Highlight selected item
            if (i == selected_idx)
                DrawRectangle(PADDING, y_offset - 2, WIDTH - 2 * PADDING, FONT_SIZE + 8, BLUE);

            // Draw item text
            DrawTextEx(
                app_font,
                items[i].text,
                (Vector2){PADDING + 10, y_offset},
                FONT_SIZE,
                1,
                WHITE);

            y_offset += FONT_SIZE + 12;
            visible_count++;
        }
    }

    // Scroll hint
    if (match_count > MAX_VISIBLE)
    {
        const char *scroll_hint = "↑/↓: Nav | Enter: Run | Esc: Quit";
        float hint_width = MeasureTextEx(app_font, scroll_hint, 16, 1).x;
        DrawTextEx(
            app_font,
            scroll_hint,
            (Vector2){WIDTH - PADDING - hint_width - 5, HEIGHT - PADDING - 20},
            16,
            1,
            GRAY);
    }

    EndDrawing();
}

/**
 * Load system font (with fallback to Raylib default)
 */
void load_system_font()
{
    // Try to load the system font
    app_font = LoadFont(SYSTEM_FONT_PATH);

    // If loading fails (e.g., wrong path), use Raylib's default font
    if (app_font.baseSize == 0)
    {
        fprintf(stderr, "Warning: Could not load system font at %s\n", SYSTEM_FONT_PATH);
        fprintf(stderr, "Falling back to Raylib default font\n");
        app_font = GetFontDefault();
    }
    else
    {
        fprintf(stdout, "Successfully loaded system font: %s\n", SYSTEM_FONT_PATH);
    }
}

int main()
{
    SetTraceLogLevel(LOG_NONE);
    InitWindow(WIDTH, HEIGHT, "rmenu");
    SetTargetFPS(60);

    // Load system font (run before rendering)
    load_system_font();

    // Read items and initialize filter
    read_items_from_stdin();
    filter_items();

    // Main loop
    while (running && !WindowShouldClose())
    {
        handle_input();
        render_main_window();
    }

    // Cleanup: Unload font only if it's a custom system font (not default)
    UnloadFont(app_font);

    CloseWindow();
    return 0;
}
